<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="0">
    <title>魔方公式查看器</title>
    <script>
        const APP_VERSION = '1.0.3';
        document.write('<link rel="stylesheet" href="styles.css?v=' + APP_VERSION + '">');
    </script>
    <style>
        .cube-container, .cube-container-img {
            display: block;
            margin: 0 auto;
            max-width: 100%;
        }

        /* 响应式样式 */
        @media (max-width: 768px) {
            .cube-viewer p {
                overflow: visible; /* 允许图片超出容器 */
            }

            .cube-viewer {
                padding: 15px;
                box-sizing: border-box;
            }

            #category-selector {
                width: 100%;
                padding: 12px;
                font-size: 18px;
                margin-bottom: 20px;
            }

            .cube-container, .cube-container-img {
                max-width: 100%;
                height: auto;
                margin: 0 auto;
                touch-action: manipulation;
                /* 优化触摸体验 */
            }

            .cube-viewer {
                touch-action: manipulation;
                -webkit-touch-callout: none;
                /* 禁用长按菜单 */
            }

            p {
                font-size: 16px;
                line-height: 1.5;
            }
           
        }
        

    </style>
</head>

<body tabindex="-1">
    
    <div class="cube-viewer">
        <select id="category-selector">
            <option value="all">所有公式</option>
            <option value="f2l">F2L (41个)</option>
            <option value="oll">OLL (57个)</option>
            <option value="pll">PLL (21个)</option>
            <option value="ollUse">oll常用（0个）</option>
            <option value="oll_dys">oll_dys (57个)</option>
        </select>
        <p>
            <canvas class="cube-container"></canvas>
            <canvas class="cube-container-img"></canvas>
        </p>
        <div id="formula-description" style="margin: 10px 0; background-color: rgba(0, 0, 0, 0.7); color: white; padding: 10px; border-radius: 5px; font-size: 24px;"></div>
        <p>按空格键或点击/触摸屏幕切换公式</p>
    </div>

    <script>
        // 修改为按实际黑色实线分割并分类
        // const img = new Image();
        // img.src = 'cfop.webp';
        const MAGNIFICATION_RATE = 2.5;

        // 定义各类别格子尺寸、起始坐标和行列数量 (根据实际图片测量)
        // 从cfop.json获取区域配置并计算格子尺寸
        // 全局配置和状态变量
        let categoryConfig = {
            'f2l': { hide_width: 0 },
            'oll': { hide_width: 0 },
            'pll': { hide_width: 0 },
            'oll_dys': { hide_width: 100 }
        };
        let ignorePoints = [];
        let ollUsePoints = [];
        let pllAaArea = null;
        let categoryPositions = { f2l: [], oll: [], pll: [], ollUse: [] };
        let detailData = {};
        function load_json() {
            // 加载cfop.json配置
            Promise.all([
                fetch(`cfop.json?v=${APP_VERSION}`).then(response => response.json()),
                fetch(`detail.json?v=${APP_VERSION}`).then(response => response.json())
            ]).then(([cfopData, detailResponse]) => {
                detailData = detailResponse;
                const data = cfopData;
                // 解析配置数据
                data.shapes.forEach(shape => {
                    if (shape.shape_type === 'rectangle') {
                        const [x1, y1] = shape.points[0];
                        const [x2, y2] = shape.points[1];
                        const label = shape.label; // 标签

                        if (label === 'f2l' || label === 'oll' || label === 'pll') {
                            categoryConfig[label] = {
                                startX: x1,
                                startY: y1,
                                width: x2 - x1,
                                height: y2 - y1,
                                // 行列配置可外部化或根据实际内容自动计算
                                rows: label === 'f2l' ? 7 : label === 'oll' ? 10 : 10,
                                cols: label === 'f2l' ? 6 : label === 'oll' ? 6 : 2
                            };
                        } else if (label === 'pll_aa') {
                            pllAaArea = {
                                startX: x1,
                                startY: y1,
                                width: x2 - x1,
                                height: y2 - y1
                            };
                        }


                    } else if (shape.shape_type === 'point' && shape.label.startsWith('ignore_p')) {
                        const [x, y] = shape.points[0];
                        ignorePoints.push({ x, y });
                    } else if (shape.shape_type === 'point' && shape.label === 'oll_use') {
                        const [x, y] = shape.points[0];
                        ollUsePoints.push({ x, y });
                    }
                });

                // 解析隐藏区域配置
                data.shapes.forEach(shape => {
                    if (shape.label.startsWith('hide_')) {
                        const label = shape.label.split('_')[1];
                        const [x1, y1] = shape.points[0];
                        const [x2, y2] = shape.points[1];
                        categoryConfig[label]['hide_width'] = x2 - x1;
                    }
                });
                // window.hideConfig = hideConfig;
                // 配置解析完成后初始化位置数据
                //categoryPositions = initCategoryPositions();
                
            
                // 加载图片
                window.mainImage = new Image();
                window.mainImage.src = `${data.imagePath}?v=${APP_VERSION}`;
                window.mainImage.onload = function () {
                    window.imageLoaded = true;
                    console.log('Image loaded successfully');
                    const cubeContainer = document.querySelector('.cube-container');
                    // cubeContainer.setAttribute('tabindex', '0');
                    // cubeContainer.focus();
                    cubeContainer.style.backgroundImage = `url('${data.imagePath}?v=${APP_VERSION}')`;
                    calculatePositions();
                    // 为f2l分类的cell计算并存储反向公式
                    calculateReverseFormulas();

                    // // 显示公式说明
                    // let descriptionDiv = document.getElementById('formula-description');
                    // if (!descriptionDiv) {
                    //     descriptionDiv = document.createElement('div');
                    //     descriptionDiv.id = 'formula-description';
                    //     descriptionDiv.style.margin = '10px 0';
                    //     descriptionDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
                    //     descriptionDiv.style.color = 'white';
                    //     descriptionDiv.style.padding = '10px';
                    //     descriptionDiv.style.borderRadius = '5px';
                    //     descriptionDiv.style.fontSize = '26px';
                    //     // 将说明插入到指定段落前面
                    //     const targetParagraph = document.querySelector('p:nth-of-type(2)');
                    //     if (targetParagraph) {
                    //         targetParagraph.parentNode.insertBefore(descriptionDiv, targetParagraph);
                    //     } else {
                    //         cubeContainer.parentNode.appendChild(descriptionDiv);
                    //     }
                    // }

                    // 打印各分类数量
                    console.log('分类数量统计:');
                    console.log('F2L:', categoryPositions.f2l.length);
                    console.log('OLL:', categoryPositions.oll.length);
                    console.log('PLL:', categoryPositions.pll.length);
                    // 更新oll常用选项数量
                    const ollUseCount = categoryPositions.ollUse.length;
                    document.querySelector('option[value="ollUse"]').textContent = `oll常用（${ollUseCount}个）`;
                    initEventListeners();
                };
            })
                .catch(error => console.error('Error loading cfop.json:', error));
        }
        // 计算gs反向公式
        function fmtGsFormula(gs) {
            if (!gs) return '';
            // 按<br/>分割公式
            const parts = gs.split('<br/>');
            const rsParts = [];
            for(let part of parts){
                const tokens = part.match(/[A-Za-z]'?[23]?'?/g) || [];
                rsParts.push(tokens.join(' '));
            }
            return rsParts.join('&nbsp;&nbsp;&nbsp;');
        }    
        // 计算gs反向公式
        function reverseGsFormula(gs) {
            if (!gs) return '';

            // 按<br/>分割公式
            const parts = gs.split('<br/>');
            const reversedParts = [];

            // 从后往前处理每个部分
            for (let i = parts.length - 1; i >= 0; i--) {
                let part = parts[i];
                let reversedPart = '';
                // 提取所有 tokens (字母+可选后缀)
                const tokens = part.match(/[A-Za-z]'?[23]?'?/g) || [];
                // 反转 tokens 顺序并处理每个 token
                const reversedTokens = tokens.reverse().map(token => {
                    if (token.endsWith('3')) {
                        return token.slice(0, -1); // 移除3
                    } else if (token.endsWith('2')) {
                        return token; // 保持2不变
                    } else if (token.endsWith("'")) {
                        return token.slice(0, -1); // 移除'
                    } else {
                        return token + "'"; // 添加'
                    }
                });
                reversedPart = reversedTokens.join(' ');
                reversedParts.push(reversedPart);
            }

            return reversedParts.join('&nbsp;&nbsp;&nbsp;');
        }

        // 实际分割逻辑 - 从左到右列优先截取
        function calculatePositions() {
            const categorySizes = categoryConfig;
            // 重置位置数据
            //categoryPositions = initCategoryPositions();

            // 清除之前的位置数据
            categoryPositions.f2l.length = 0;
            categoryPositions.oll.length = 0;
            categoryPositions.pll.length = 0;

            // 处理每个类别
            Object.keys(categorySizes).forEach(category => {
                const config = categorySizes[category];
                // const totalCells = category === 'f2l' ? 42 : category === 'oll' ? 60 : 16;
                let cellCount = 0;

                // 列优先截取 (从左到右)
                for (let col = 0; col < config.cols; col++) {
                    for (let row = 0; row < config.rows; row++) {
                        // if (cellCount >= totalCells) break;

                        const cellX = config.startX + col * (config.width / config.cols);
                        const cellY = config.startY + row * (config.height / config.rows);
                        const cellWidth = config.width / config.cols;
                        const cellHeight = config.height / config.rows;

                        // 检查是否需要忽略当前格子
                        const shouldIgnore = ignorePoints.some(point =>
                            point.x >= cellX && point.x <= cellX + cellWidth &&
                            point.y >= cellY && point.y <= cellY + cellHeight
                        );

                        if (!shouldIgnore) {
                            const cell = { x: -cellX, y: -cellY, width: cellWidth, height: cellHeight, label: category };
                            categoryPositions[category].push(cell);

                            // 检查是否为OLL常用项
                            if (category === 'oll') {
                                const isUsed = ollUsePoints.some(point =>
                                    point.x >= cellX && point.x <= cellX + cellWidth &&
                                    point.y >= cellY && point.y <= cellY + cellHeight
                                );
                                if (isUsed) {
                                    categoryPositions.ollUse.push(cell);
                                }
                            }
                            cellCount++;
                        }
                    }
                }

                // 添加pll_aa区域
                if (category === 'pll' && pllAaArea) {
                    categoryPositions[category].unshift({
                        x: -pllAaArea.startX,
                        y: -pllAaArea.startY,
                        width: pllAaArea.width,
                        height: pllAaArea.height,
                        label: category
                    });
                }
            });
            
            
        }
        
        // 为f2l分类的cell计算并存储反向公式
        function calculateReverseFormulas() {
            if(detailData && detailData.f2l){
                categoryPositions.f2l.forEach((cell, index) => {
                    const item = detailData.f2l[index];
                    if (item && item.gs) {
                        item.rv = reverseGsFormula(item.gs); // 计算并存储反向公式
                        item.gs=fmtGsFormula(item.gs)
                    }
                });
            }
            if(detailData && detailData.oll){
                categoryPositions.oll.forEach((cell, index) => {
                    const item = detailData.oll[index];
                    if (item && item.gs) {
                        item.rv = reverseGsFormula(item.gs); // 计算并存储反向公式
                        item.gs=fmtGsFormula(item.gs)
                    }
                });
            }
            if(detailData && detailData.pll){
                categoryPositions.pll.forEach((cell, index) => {
                    const item = detailData.pll[index];
                    if (item && item.gs) {
                        item.rv = reverseGsFormula(item.gs); // 计算并存储反向公式
                        item.gs=fmtGsFormula(item.gs)
                    }
                });
            }
            // 处理oll_dys分类的反向公式
            if(detailData && detailData.oll_dys){
                detailData.oll_dys.forEach(item => {
                    if (item && item.gs) {
                        item.rv = reverseGsFormula(item.gs);
                        item.gs = fmtGsFormula(item.gs);
                    }
                });
            }
        }

        function initEventListeners() {
            const cubeContainer = document.querySelector('.cube-container');
            const categorySelector = document.getElementById('category-selector');
            let currentCategory = 'all';
            let currentCells = [];
            let lastRandomIndex = null;

            function updateCells() {
                currentCells = currentCategory === 'all'
                    ? [...categoryPositions.f2l, ...categoryPositions.oll, ...categoryPositions.pll]
                    : currentCategory === 'ollUse'
                    ? categoryPositions.ollUse || []
                    : currentCategory === 'oll_dys'
                    ? Array.from({length: 57}, (_, i) => ({label: 'oll_dys', index: i + 1}))
                    : categoryPositions[currentCategory] || [];
                // window.showingHidden = true;
                showRandomCell();
            }

            function showRandomCell(useLastIndex = false, showAnimation = true, showHidden = true) {
                window.showingHidden = showHidden;
                if (!window.imageLoaded) {
                    console.log('Image not loaded yet, waiting...');
                    setTimeout(showRandomCell, 100);
                    return;
                }
                if (currentCells.length === 0) return;
                let randomIndex;
                if (useLastIndex && lastRandomIndex !== null) {
                    randomIndex = lastRandomIndex;
                } else {
                    // 生成与上次不同的随机索引
                    do {
                        randomIndex = Math.floor(Math.random() * currentCells.length);
                    } while (currentCells.length > 1 && randomIndex === lastRandomIndex);
                }
                lastRandomIndex = randomIndex;
                const cell = currentCells[randomIndex];
                const cubeContainer = document.querySelector('.cube-container');
                const cubeContainerImg = document.querySelector('.cube-container-img');
                const ctx = cubeContainer.getContext('2d');
                const ctxImg = cubeContainerImg.getContext('2d');


                // 仅在需要显示动画时添加类
                if (showAnimation) {
                    cubeContainer.classList.add('flipping');
                }

                // 获取当前分类的隐藏宽度
                //nst currentHideWidth = window.hideConfig[currentCategory]?.width || 0;
                //cot effectiveWidth = showHidden ? cell.width : cell.width - currentHideWidth;

                // 动画结束后更新背景并移除类
                const updateCanvas = () => {
                    // 设置canvas尺寸为裁剪区域的2.5倍
                    cubeContainer.width = cell.width * MAGNIFICATION_RATE;
                    cubeContainer.height = cell.height * MAGNIFICATION_RATE;
                    // 清除画布
                    ctx.clearRect(0, 0, cubeContainer.width, cubeContainer.height);
                    // 裁剪并绘制图片
                    if (currentCategory === 'oll_dys') {
                            oll_dys_img = new Image();

                            oll_dys_img.src = `oll_dys/${String(cell.index).padStart(2, '0')}.png`;
                            // 添加高质量放大功能
                            oll_dys_img.onload = function() {
                                console.log('oll_dys_img loaded:', oll_dys_img.width, oll_dys_img.height);
                                cubeContainerImg.width = oll_dys_img.width * MAGNIFICATION_RATE;
                                cubeContainerImg.height = oll_dys_img.height * MAGNIFICATION_RATE;
                                // 清除画布
                                ctxImg.clearRect(0, 0, cubeContainerImg.width, cubeContainerImg.height);
                                // 确保图片加载完成后再绘制
                                ctxImg.drawImage(
                                    oll_dys_img,
                                    0, 0,
                                    oll_dys_img.width, oll_dys_img.height,
                                    0, 0,
                                    cubeContainerImg.width, cubeContainerImg.height
                                );
                                cubeContainer.style.display = 'none';
                                cubeContainerImg.style.display = 'block';
                            };
                            // 处理图片加载错误
                            oll_dys_img.onerror = function() {
                                console.error('Failed to load image:', oll_dys_img.src);
                                cubeContainer.style.display = 'none';
                            };
                        } else {
                            ctx.drawImage(
                                window.mainImage,
                                Math.abs(cell.x), Math.abs(cell.y),
                                cell.width, cell.height,
                                0, 0,
                                cubeContainer.width, cubeContainer.height
                            );
                            // document.getElementById('oll-dys-image').style.display = 'none';
                            cubeContainer.style.display = 'block';
                            cubeContainerImg.style.display = 'none';

                        }
                    let descriptionDiv = document.getElementById('formula-description');
                    if (showHidden) {
                        // 获取当前分类的隐藏宽度
                        const hide_width = categoryConfig[cell.label]['hide_width'];
                        const hiddenX = hide_width;
                        const hiddenY = 0;
                        console.log('showHidden', showHidden, hide_width);
                        //用半透明颜色来遮挡隐藏部分
                        ctx.fillStyle = 'rgba(0, 0, 0, 0.9)';
                        ctx.fillRect(hiddenX * MAGNIFICATION_RATE, hiddenY * MAGNIFICATION_RATE, (cell.width - hide_width) * MAGNIFICATION_RATE, cell.height * MAGNIFICATION_RATE);

                        

                        // 获取当前分类和索引
                        let category, index;
                        if (currentCategory === 'ollUse') {
                            category = 'oll';
                            index = categoryPositions[category].indexOf(cell);
                        } else if (currentCategory === 'oll_dys') {
                            category = 'oll_dys';
                            index = cell.index - 1; // 转换为0-based索引
                        } else {
                            category = cell.label;
                            index = categoryPositions[category].indexOf(cell);
                        }

                        const item = detailData[category] && detailData[category][index] ? detailData[category][index] : null;
                        let descriptionText = '';
                        if (item) {
                            // 显示公式、反向公式和描述
                            descriptionText += '公式: ' + (item.gs || 'N/A') + '<br/>';
                            descriptionText += '反向: ' + (item.rv || 'N/A') + '<br/>';
                            descriptionText += '描述: ' + (item.description || '无描述信息');
                        } else {
                            descriptionText = '公式: N/A<br/>反向: N/A<br/>描述: 无相关信息';
                        }
                        descriptionDiv.innerHTML = descriptionText + "(" + category + ")";
                    }
                    descriptionDiv.style.display = !showHidden ? 'block' : 'none';
                    // 仅在显示动画时移除类
                    if (showAnimation) {
                        cubeContainer.classList.remove('flipping');
                    }
                };

                if (showAnimation) {
                    setTimeout(updateCanvas, 600); // 与动画时长匹配
                } else {
                    updateCanvas(); // 立即更新，不等待动画
                }
            }

            updateCells();
            categorySelector.addEventListener('change', () => {
                currentCategory = categorySelector.value;
                updateCells();
            });
            document.addEventListener('keydown', (e) => {
                if (e.code === 'Space') {
                    e.preventDefault();
                    handleInteraction();
                }
            });

            // 添加点击事件监听
            // 同时支持点击和触摸事件
            const handleInteraction = () => {
                if (window.showingHidden) {
                    // 第二次按空格，使用上次索引，不显示动画
                    showRandomCell(true, false, false);
                } else {
                    // 第二次按空格，使用上次索引，不显示动画
                    showRandomCell(false, true, true);
                }
            };
            document.addEventListener('click', (e) => {
                e.preventDefault(); // 防止触摸时的默认行为（如缩放）
                handleInteraction();
            });
            // document.addEventListener('touchstart', (e) => {
            //     e.preventDefault(); // 防止触摸时的默认行为（如缩放）
            //     handleInteraction();
            // });

            // 窗口大小变化时重新计算并显示当前格子
            window.addEventListener('resize', () => {
                if (lastRandomIndex !== null) {
                    showRandomCell(true, false, window.showingHidden);
                }
            });

        }
        // window.mainImage = new Image();
        // window.mainImage.onload = function() {
        //     console.log('Image loaded successfully');
        //     if (typeof calculatePositions === 'function') {
        //         calculatePositions();
        //         initEventListeners();
        //     }
        // };
        window.onload = function () {
            load_json();
            document.body.focus();
        };
    </script>
</body>

</html>